﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using Rhino.Mocks;
using Rhino.Mocks.Interfaces;

using SharedGenomics.Domain;
using SharedGenomics.Workbench.Views;
using SharedGenomics.Workbench.Presenters;
using SharedGenomics.Workbench.PresentationModel;

namespace SharedGenomics.Workbench.Tests.Presenters
{
    [TestClass]
    public class CategoryFilterSetPresenterTests
    {
        private MockRepository mocks;
        private ICategoryFilterSetView view;
        private CategoryFilterPresenterFactory presenterFactory;
        private FileSchema schema;

        #region Setup

        [TestInitialize]
        public void Setup()
        {
            this.mocks = new MockRepository();
            this.view = this.mocks.Stub<ICategoryFilterSetView>();
            this.presenterFactory = this.mocks.Stub<CategoryFilterPresenterFactory>(null, null);

            this.schema = new FileSchema();
            this.schema.AddIdColumn("id");
            this.schema.AddGenderColumn("gender", "M", "F");
        }

        #endregion

        #region Constructor Tests

        [TestMethod]
        public void ConstructorTest_RegistersForViewEvents()
        {
            ICategoryFilterSetView view = this.mocks.DynamicMock<ICategoryFilterSetView>();

            using (this.mocks.Record())
            {
                view.AddCategoryClicked += null;
                LastCall.IgnoreArguments();
            }

            using (this.mocks.Playback())
            {
                var presenter = new CategoryFilterSetPresenter(view, null);
            }
        }

        [TestMethod]
        public void ConstructorTest_SetsUpInitialViewState()
        {
            ICategoryFilterSetView view = this.mocks.DynamicMock<ICategoryFilterSetView>();

            using (this.mocks.Record())
            {
                view.IsAddCategoryEnabled = false;
            }

            using (this.mocks.Playback())
            {
                var presenter = new CategoryFilterSetPresenter(view, null);
            }
        }

        #endregion

        #region Property Tests

        [TestMethod]
        public void SetSchema_EnablesAddCategory()
        {
            this.view = this.mocks.DynamicMock<ICategoryFilterSetView>();
            this.view.IsAddCategoryEnabled = false;

            this.mocks.ReplayAll();

            var presenter = this.CreatePresenter();
            presenter.Schema = this.schema;
        }

        [TestMethod]
        public void SetSchema_Null_DisablesAddCategory()
        {
            this.view = this.mocks.DynamicMock<ICategoryFilterSetView>();
            this.view.IsAddCategoryEnabled = false;

            this.mocks.ReplayAll();

            var presenter = this.CreatePresenter();
            presenter.Schema = null;
        }

        #endregion

        #region Event Handler Tests

        [TestMethod]
        public void AddCategoryClickedTest_AddsToView()
        {
            IEventRaiser clickedEvent;
            this.view = this.mocks.DynamicMock<ICategoryFilterSetView>();
            ICategoryFilterView filterView = this.mocks.Stub<ICategoryFilterView>();
            var fp = this.CreateFilterPresenter();

            using (this.mocks.Record())
            {
                this.view.AddFilterView(null);
                LastCall.IgnoreArguments();
                SetupResult.For(this.presenterFactory.CreateNew(null, null)).IgnoreArguments().Return(fp);

                this.view.AddCategoryClicked += null;
                clickedEvent = LastCall.IgnoreArguments().GetEventRaiser();
            }

            using (this.mocks.Playback())
            {
                var presenter = this.CreatePresenter();
                clickedEvent.Raise(null, null);
            }
        }

        [TestMethod]
        public void AddCategoryClickedTest_RegistersForCloseEvent()
        {
            IEventRaiser clickedEvent;
            ICategoryFilterView filterView = this.mocks.Stub<ICategoryFilterView>();
            var fp = this.CreateFilterPresenter();

            using (this.mocks.Record())
            {
                SetupResult.For(this.presenterFactory.CreateNew(null, null)).IgnoreArguments().Return(fp);

                this.view.AddCategoryClicked += null;
                clickedEvent = LastCall.IgnoreArguments().GetEventRaiser();
            }

            using (this.mocks.Playback())
            {
                var presenter = this.CreatePresenter();
                clickedEvent.Raise(null, null);

                Assert.AreEqual(1, fp.CloseEventSubscriberCount);
            }
        }

        [TestMethod]
        public void RemoveFilterTest_FilterViewRemovedFromMainView()
        {
            this.view = this.mocks.DynamicMock<ICategoryFilterSetView>();
            var filterPresenter = this.CreateFilterPresenter();

            using (this.mocks.Record())
            {
                this.view.RemoveFilterAt(0);
            }

            using (this.mocks.Playback())
            {
                var presenter = this.CreatePresenter();
                presenter.AddCategoryFilter(filterPresenter);

                filterPresenter.RaiseFilterRemovedEvent();
            }
        }

        #endregion

        private CategoryFilterSetPresenter CreatePresenter()
        {
            return new CategoryFilterSetPresenter(this.view, this.presenterFactory) { Schema = this.schema };
        }

        private MockCategoryFilterPresenter CreateFilterPresenter()
        {
            ICategoryFilterView categoryFilterView = this.mocks.Stub<ICategoryFilterView>();
            CategoryValueViewFactory valueViewFactory = this.mocks.Stub<CategoryValueViewFactory>();

            return new MockCategoryFilterPresenter(categoryFilterView, valueViewFactory);
        }

        private T CreateItem<T>(Func<object[], T> createFunc, params object[] constructorArgs)
        {
            return createFunc(constructorArgs);
        }

        #region MockCategoryFilterPresenter

        private class MockCategoryFilterPresenter : CategoryFilterPresenter
        {
            public MockCategoryFilterPresenter(ICategoryFilterView view, CategoryValueViewFactory valueViewFactory)
                : base(view, valueViewFactory, null, s => new ValueDisplayItem<string>[] { })
            {
            }

            public int CloseEventSubscriberCount
            {
                get
                {
                    //NOTE: brittle...
                    var eventFieldInfo = this.GetType().BaseType.GetField("FilterRemoved", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                    MulticastDelegate d = (MulticastDelegate)eventFieldInfo.GetValue(this);
                    return d.GetInvocationList().Length;
                }
            }

            public void RaiseFilterRemovedEvent()
            {
                this.OnFilterRemoved();
            }
        }

        #endregion
    }
}
